﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;

namespace xjjxmm.infrastructure.ToolKit
{
    public class XmlKit
    {
        /// <summary>
        /// 数据对象序列化二进制数据流
        /// </summary>
        /// <param name="stream">数据二进制流</param>
        /// <param name="o">数据对象</param>
        /// <param name="encoding">编码方式</param>
        private static void SerializeInternal(Stream stream, object o, Encoding encoding)
        {
            if (o == null)
                throw new ArgumentNullException("o");
            if (encoding == null)
                throw new ArgumentNullException("encoding");
            XmlSerializer serializer = new XmlSerializer(o.GetType());
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.NewLineChars = "\r\n";
            settings.Encoding = encoding;
            settings.IndentChars = "    ";
            using (XmlWriter writer = XmlWriter.Create(stream, settings))
            {
                serializer.Serialize(writer, o);
                writer.Close();
            }
        }

        /// <summary>
        /// 数据对象序列化字符串
        /// </summary>
        /// <param name="o">数据对象</param>
        /// <returns></returns>
        public static string SerializeToString(object o)
        {
            return SerializeToString(o, Encoding.UTF8);
        }

        /// <summary>
        /// 数据对象序列化字符串
        /// </summary>
        /// <param name="o">数据对象</param>
        /// <param name="encoding">编码方式</param>
        /// <returns></returns>
        public static string SerializeToString(object o, Encoding encoding)
        {
            using (MemoryStream stream = new MemoryStream())
            {
                SerializeInternal(stream, o, encoding);

                stream.Position = 0;
                using (StreamReader reader = new StreamReader(stream, encoding))
                {
                    return reader.ReadToEnd();
                }
            }
        }

        /// <summary>
        /// 文件序列化二进制数据流
        /// </summary>
        /// <param name="o">数据对象</param>
        /// <param name="path">文件路径</param>
        public static void SerializeToFile(object o, string path)
        {
            SerializeToFile(o, path, Encoding.UTF8);
        }

        /// <summary>
        /// 文件序列化二进制数据流
        /// </summary>
        /// <param name="o">数据对象</param>
        /// <param name="path">文件路径</param>
        /// <param name="encoding">编码方式</param>
        public static void SerializeToFile(object o, string path, Encoding encoding)
        {
            if (string.IsNullOrEmpty(path))
                throw new ArgumentNullException("path");

            using (FileStream file = new FileStream(path, FileMode.Create, FileAccess.Write))
            {
                SerializeInternal(file, o, encoding);
            }
        }

        /// <summary>
        /// 将文件反序列化数据对象
        /// </summary>
        /// <typeparam name="T">数据对象</typeparam>
        /// <param name="path">文件路径</param>
        /// <returns></returns>
        public static T? DeserializeFromFile<T>(string path)
        {
            return DeserializeFromFile<T>(path, Encoding.UTF8);
        }

        /// <summary>
        /// 将文件反序列化数据对象
        /// </summary>
        /// <typeparam name="T">数据对象</typeparam>
        /// <param name="path">文件路径</param>
        /// <param name="encoding">编码方式</param>
        /// <returns></returns>
        public static T? DeserializeFromFile<T>(string path, Encoding encoding)
        {
            if (string.IsNullOrEmpty(path))
                throw new ArgumentNullException("path");
            if (encoding == null)
                throw new ArgumentNullException("encoding");
            string xml = File.ReadAllText(path, encoding);
            return DeserializeFromString<T>(xml, encoding);
        }

        /// <summary>
        /// 将字符反序列化数据对象
        /// </summary>
        /// <typeparam name="T">编码方式</typeparam>
        /// <param name="s">字符</param>
        /// <returns>对象</returns>
        public static T? DeserializeFromString<T>(string s)
        {
            return DeserializeFromString<T>(s, Encoding.UTF8);
        }

        /// <summary>
        /// 反序列化
        /// </summary>
        /// <param name="type">类型</param>
        /// <param name="s">字符串</param>
        /// <returns>对象</returns>
        public static object? DeserializeFromString(Type type, string s)
        {
            return DeserializeFromString(type, s, Encoding.UTF8);
        }

        /// <summary>
        /// 反序列化
        /// </summary>
        /// <param name="type">类型</param>
        /// <param name="s">字符串</param>
        /// <param name="encoding">编码方式</param>
        /// <returns>对象</returns>
        public static object? DeserializeFromString(Type type, string s, Encoding encoding)
        {
            if (string.IsNullOrEmpty(s))
                throw new ArgumentNullException("string");
            if (encoding == null)
                throw new ArgumentNullException("encoding");

            var mySerializer = new XmlSerializer(type);
            using (MemoryStream ms = new MemoryStream(encoding.GetBytes(s)))
            {
                using (StreamReader sr = new StreamReader(ms, encoding))
                {
                    return mySerializer.Deserialize(sr);
                }
            }
        }

        /// <summary>
        /// 将字符反序列化数据对象
        /// </summary>
        /// <typeparam name="T">数据对象</typeparam>
        /// <param name="s">字符串</param>
        /// <param name="encoding">编码方式</param>
        /// <returns>对象</returns>
        public static T? DeserializeFromString<T>(string s, Encoding encoding)
        {
            return (T?)DeserializeFromString(typeof(T), s, encoding);
            //if (string.IsNullOrEmpty(s))
            //    throw new ArgumentNullException("string");
            //if (encoding == null)
            //    throw new ArgumentNullException("encoding");

            //XmlSerializer mySerializer = new XmlSerializer(typeof(T));
            //using (MemoryStream ms = new MemoryStream(encoding.GetBytes(s)))
            //{
            //    using (StreamReader sr = new StreamReader(ms, encoding))
            //    {
            //        return (T)mySerializer.Deserialize(sr);
            //    }
            //}
        }

        /// <summary>
        /// 将资源文件反序列化数据对象
        /// </summary>
        /// <typeparam name="T">数据对象</typeparam>
        /// <param name="name">资源文件名称</param>
        /// <returns></returns>
        public static T? DeserializeFromResource<T>(string name)
        {
            return DeserializeFromResource<T>(name, Encoding.UTF8);
        }

        /// <summary>
        /// 将资源文件反序列化数据对象
        /// </summary>
        /// <typeparam name="T">数据对象</typeparam>
        /// <param name="name">资源文件名称</param>
        /// <param name="encoding">编码方式</param>
        /// <returns>资源对象</returns>
        public static T? DeserializeFromResource<T>(string name, Encoding encoding)
        {
            return DeserializeFromResource<T>(typeof(XmlKit), name, encoding);
        }

        /// <summary>
        /// 将资源文件反序列化数据对象
        /// </summary>
        /// <typeparam name="T">数据对象</typeparam>
        /// <param name="type">对象类型</param>
        /// <param name="name">资源文件名称</param>
        /// <param name="encoding">编码方式</param>
        /// <returns>资源对象</returns>
        public static T? DeserializeFromResource<T>(Type type, string name, Encoding encoding)
        {
            return DeserializeFromResource<T>(Assembly.GetAssembly(type), name, encoding);
        }

        /// <summary>
        /// 将资源文件反序列化数据对象
        /// </summary>
        /// <typeparam name="T">数据对象</typeparam>
        /// <param name="type">对象类型</param>
        /// <param name="name">资源文件名称</param>
        /// <returns>资源对象</returns>
        public static T? DeserializeFromResource<T>(Type type, string name)
        {
            return DeserializeFromResource<T>(Assembly.GetAssembly(type), name, Encoding.UTF8);
        }

        /// <summary>
        /// 将资源文件反序列化数据对象
        /// </summary>
        /// <typeparam name="T">数据对象</typeparam>
        /// <param name="assembly">程序集</param>
        /// <param name="name">资源文件名称</param>
        /// <param name="encoding">编码方式</param>
        /// <returns>资源对象</returns>
        public static T? DeserializeFromResource<T>(Assembly? assembly, string name, Encoding encoding)
        {
            Stream? stream = assembly?.GetManifestResourceStream(name);
            return DeserializeFromStream<T>(stream, encoding);
        }

        /// <summary>
        /// 将资源文件反序列化数据对象
        /// </summary>
        /// <typeparam name="T">数据对象</typeparam>
        /// <param name="assembly">程序集</param>
        /// <param name="name">资源文件名称</param>
        /// <returns>资源对象</returns>
        public static T? DeserializeFromResource<T>(Assembly? assembly, string name)
        {
            Stream? stream = assembly?.GetManifestResourceStream(name);
            return DeserializeFromStream<T>(stream, Encoding.UTF8);
        }

        /// <summary>
        /// 将二进制数据流反序列化数据对象
        /// </summary>
        /// <typeparam name="T">数据对象</typeparam>
        /// <param name="s">二进制流</param>
        /// <param name="encoding">编码方式</param>
        /// <returns></returns>
        public static T? DeserializeFromStream<T>(Stream? s, Encoding encoding)
        {
            if (s == null)
                throw new ArgumentNullException("stream");
            if (encoding == null)
                throw new ArgumentNullException("encoding");
            XmlSerializer mySerializer = new XmlSerializer(typeof(T));
            using (StreamReader sr = new StreamReader(s, encoding))
            {
                return (T?)mySerializer.Deserialize(sr);
            }
        }

        /// <summary>
        /// 获取节点属性
        /// </summary>
        /// <param name="node">节点</param>
        /// <param name="id">属性ID</param>
        /// <returns>属性值</returns>
        public static string GetAttribute(XmlNode node, string id)
        {
            if (node == null)
            {
                throw new Exception("XmlNode is null.");
            }
            if (node.NodeType != XmlNodeType.Element)
            {
                throw new Exception(string.Format("Invalidate XmlNode({0}).", node.Name));
            }

            XmlAttribute? attribute = node?.Attributes?[id];
            if (attribute == null)
            {
                throw new Exception(string.Format("Attribute({0}) is undefined.", id));
            };
            return attribute.Value;
        }

        /// <summary>
        /// 缩进字符串
        /// </summary>
        public const char Format_IndentChar = ' ';
        /// <summary>
        /// 缩进字符串个数
        /// </summary>
        public const int Format_Indentation = 4;

        /// <summary>
        /// Xml格式化输出
        /// </summary>
        /// <param name="xml">Xml字符串</param>
        /// <returns>格式化的Xml字符串</returns>
        public static string Format(string xml, int indentation = Format_Indentation, char indentChar = Format_IndentChar)
        {
            XmlDocument xd = new XmlDocument();
            xd.LoadXml(xml);
            StringBuilder sb = new StringBuilder();
            StringWriter sw = new StringWriter(sb);
            XmlTextWriter? xtw = null;
            try
            {
                xtw = new XmlTextWriter(sw);
                xtw.Formatting = Formatting.Indented;
                xtw.Indentation = 4;
                xtw.IndentChar = ' ';
                xd.WriteTo(xtw);
            }
            finally
            {
                if (xtw != null)
                    xtw.Close();
            }
            return sb.ToString();
        }
    }
}
